package utils

import implicits.Implicits._
import org.apache.commons.io.FileUtils
import org.apache.commons.lang3.StringUtils
import org.joda.time.DateTime
import play.api.libs.json.Json
import shapeless._
import tool.Pojo.KeywordInfo
import tool.Tool

import java.io.File
import java.lang.reflect.Field
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}

/**
 * Created by yz on 17/9/2021
 */
object Utils {

  def isWindows = {
    System.getProperty("os.name") match {
      case x if x.contains("Windows") => true
      case _ => false
    }
  }

  def getArrayByTs[T, H <: HList](x: Seq[T])(implicit generic: LabelledGeneric.Aux[T, H],
                                             hc: Lazy[ObjectCodec[H]]) = {
    x.map { y =>
      y.myAsJson
    }
  }

  def deleteDirectory(direcotry: File) = {
    try {
      FileUtils.deleteDirectory(direcotry)
    } catch {
      case _ =>
    }
  }

  def execFuture[T](f: Future[T]): T = {
    Await.result(f, Duration.Inf)
  }

  def t2ConfigFile[T, H <: HList](workspaceDir: File, data: T)(implicit generic: LabelledGeneric.Aux[T, H],
                                                               hc: Lazy[ObjectCodec[H]]

  ): File = {
    val configFile = Tool.getConfigFile(workspaceDir)
    t2ConfigFile(data, configFile)
  }

  def t2ConfigFile[T, H <: HList](data: T, configFile: File)(implicit generic: LabelledGeneric.Aux[T, H],
                                                             hc: Lazy[ObjectCodec[H]]

  ): File = {
    val json = data.myAsJson
    Json.stringify(json).toFile(configFile)
  }

  def configFile2Json(workspaceDir: File) = {
    val configFile = Tool.getConfigFile(workspaceDir)
    configFile2JsonByFile(configFile)
  }

  def configFile2JsonByFile(configFile: File) = {
    Json.parse(configFile.str)
  }

  def getJsonByTs[T, H <: HList](x: Seq[T])(implicit generic: LabelledGeneric.Aux[T, H],
                                            hc: Lazy[ObjectCodec[H]]) = {
    val array = getArrayByTs(x)
    Json.toJson(array)
  }

  def getTime(startTime: Long) = {
    val endTime = System.currentTimeMillis()
    val time = (endTime - startTime) / 1000.0
    time
  }

  def getValuesByFields[T](y: T, fields: Seq[String]) = {
    fields.map { fieldStr =>
      getValueByField(y, fieldStr)
    }
  }

  def getValueByField[T](y: T, fieldStr: String) = {
    val field: Field = y.getClass.getDeclaredField(fieldStr)
    field.setAccessible(true)
    field.get(y).toString
  }

  def getSearchMap[T](keywordsMap: Map[T, KeywordInfo], idField: String, fields: Seq[String]) = {
    keywordsMap.map { case (y, keywordInfo) =>
      val texts = Utils.getValuesByFields(y, fields)
      val field: Field = y.getClass.getDeclaredField(idField)
      field.setAccessible(true)
      val id = field.get(y).toString
      val keywords = keywordInfo.remainKeywords
      val map = keywords.map { z =>
        val matchInfo = z.toUpperCase
        (z, texts.find(_.toUpperCase.indexOf(matchInfo) != -1).getOrElse(""))
      }.filter(x => StringUtils.isNotEmpty(x._2)).toMap
      val remainKeywords = keywords.filterNot(x => map.keySet.contains(x))
      (id, KeywordInfo(remainKeywords, keywordInfo.map ++ map))
    }

  }

  def getMapByT[T](t: T) = {
    t.getClass.getDeclaredFields.toBuffer.map { x: Field =>
      x.setAccessible(true)
      val kind = x.get(t)
      val value = getValue(kind)
      (x.getName, value)
    }.init.toMap
  }

  def getValue[T](kind: T, noneMessage: String = "暂无"): String = {
    kind match {
      case x if x.isInstanceOf[DateTime] => val time = x.asInstanceOf[DateTime]
        time.toString("yyyy-MM-dd HH:mm:ss")
      case x if x.isInstanceOf[Option[T]] => val option = x.asInstanceOf[Option[T]]
        if (option.isDefined) getValue(option.get, noneMessage) else noneMessage
      case _ => kind.toString
    }
  }

  def getInfoByLines(lines: List[List[String]]) = {
    val columnNames = lines.head
    val cNames = lines.head.zipWithIndex.map { case (v, i) =>
      s"c${i}"
    }
    val map = cNames.zip(columnNames).toSeqMap
    val array = lines.drop(1).map { tmpColumns =>
      val columns = tmpColumns.map(_.trimQuote).padTo(cNames.size,"")
      cNames.zip(columns).toMap
    }
    (map, array)
  }

  val phylotreeCss =
    """
      |<style>
      |.tree-selection-brush .extent {
      |    fill-opacity: .05;
      |    stroke: #fff;
      |    shape-rendering: crispEdges;
      |}
      |
      |.tree-scale-bar text {
      |  font: sans-serif;
      |}
      |
      |.tree-scale-bar line,
      |.tree-scale-bar path {
      |  fill: none;
      |  stroke: #000;
      |  shape-rendering: crispEdges;
      |}
      |
      |.node circle, .node ellipse, .node rect {
      |fill: steelblue;
      |stroke: black;
      |stroke-width: 0.5px;
      |}
      |
      |.internal-node circle, .internal-node ellipse, .internal-node rect{
      |fill: #CCC;
      |stroke: black;
      |stroke-width: 0.5px;
      |}
      |
      |.node {
      |font: 10px sans-serif;
      |}
      |
      |.node-selected {
      |fill: #f00 !important;
      |}
      |
      |.node-collapsed circle, .node-collapsed ellipse, .node-collapsed rect{
      |fill: black !important;
      |}
      |
      |.node-tagged {
      |fill: #00f;
      |}
      |
      |.branch {
      |fill: none;
      |stroke: #999;
      |stroke-width: 2px;
      |}
      |
      |.clade {
      |fill: #1f77b4;
      |stroke: #444;
      |stroke-width: 2px;
      |opacity: 0.5;
      |}
      |
      |.branch-selected {
      |stroke: #f00 !important;
      |stroke-width: 3px;
      |}
      |
      |.branch-tagged {
      |stroke: #00f;
      |stroke-dasharray: 10,5;
      |stroke-width: 2px;
      |}
      |
      |.branch-tracer {
      |stroke: #bbb;
      |stroke-dasharray: 3,4;
      |stroke-width: 1px;
      |}
      |
      |
      |.branch-multiple {
      |stroke-dasharray: 5, 5, 1, 5;
      |stroke-width: 3px;
      |}
      |
      |.branch:hover {
      |stroke-width: 10px;
      |}
      |
      |.internal-node circle:hover, .internal-node ellipse:hover, .internal-node rect:hover {
      |fill: black;
      |stroke: #CCC;
      |}
      |
      |.tree-widget {
      |}
      |</style>
    """.stripMargin


}
